Ziel des Notebooks ist es dem Leser Zufallszahlen und die Erzeugung großer Mengen solcher Zahlen näher zu bringen. Dazu betrachten wir das Beispiel des RandomWalks, in dem wir mit Hilge von Zufallszahlen Teilchen darstellen, die sich zufällig im Raum verteilen. Zu beachten ist dabei, dass die Wahrscheinlichkeit für jeden Schritt immer gleich groß ist, bei einem zweidimensionalen Random Walk also 1/4 für jede Richtung.
Um dieses Problem in ein Python-Programm zu übersetzen nehmen wir zunächst einmal an, dass wir uns auf der x-Achse eines Koordinatensystems bewegen und alle Werte die die Position eines Teilchens auf der Achse angeben in einem Array gespeichert sind. Wir müssen außerdem zufällige Zahlen erzeugen, die angeben ob wir einen Schritt nach vorne oder nach hinten machen. Da wir einen Random Walk in zwei Dimensionen implementieren wollen brauchen wir 4 mögliche Ereignisse.
import numpy as np
import random as rd
import plotly.graph_objects as go
numpar = 1000
numstep = 900
NORTH = 1; SOUTH = 2; WEST = 3; EAST = 4
numpar legt fest wie viele Partikel genutzt werden sollen, numstep wie viele Schritte gemacht werden. mit rd.seed(42) wird der Seed für den Zufallsgenerator auf 42 gesetzt, so das wir immer gleiche Zahlen erhalten. Nort, South, West, East werden genutzt um den im Zufallsgenerator erzeugten Zahlen 1 bis 4 eine Richtung zuzuweißen
def singleStep():
position = numpy.zeros(shape=(2,), dtype=int)
currentStep = rd.randint(1, 4 + 1)
position[1] += numpy.where(currentStep == NORTH, 1, 0)
position[1] -= numpy.where(currentStep == SOUTH, 1, 0)
position[0] += numpy.where(currentStep == EAST, 1, 0)
position[0] -= numpy.where(currentStep == WEST, 1, 0)
return position
Um den Code übersichtlicher zu gestalten und die Wiederverwendbarkeit zu erhöhen, unterteilen wir unser Programm in mehrere Module. Der vorherige Codeblock wird genutzt um einen einzelnen Schritt zu machen. Da bei einem Schritt direkt mehrere Partikel "auf die Reise" gehen können übergeben wir der Methode auch die gesetzte Anzahl an Partikeln, die bei der Erstellung der Zufallszahlen in betracht gezogen werden muss.
Wir erstellen ein Numpy-Array mit dem Datentyp int, in dem die erzeugte Position gespeichert wird. Mit Hilfe von random.randint erzeugen wir die Zufallszahl. Mögliche Ergebnisse für das Ergebnis der Erzeugungprüfen wir welcher Richtung die erhaltene Zahl entspricht und weißen dem position Array einen entsprechenden Wert zu. Die X-Koordinate des Punkts weisen wir dem Index 0 des Arrays zu, die Y-Koordinate weisen wir dem Index 1 des Arrays zu. Die zugewiesene Zahlen sind dabei 0, 1 oder -1. Die in position gespeicherte Koordinate wird von der Methode zurückgegeben und kann so weiterverwendet werden.
def walkSteps(numPart=1, numstep=100):
positionsArray = numpy.zeros(shape=(numstep+1, 2), dtype=int)
for step in numpy.arange(numstep):
positionsArray[step+1, ] = positionsArray[step, ] + singleStep()
tuple = (positionsArray, numstep)
return tuple
Das nächste Modul das wir implementieren ist die Methode walkSteps, der wir die Parameter numstep und numpar übergeben.
Mit positionsArray erstellen wir ein leeres Array, in dem wir die einzelnen Schritte speichern die wir erstellt haben und mit denen wir später eine Grafik erzeugen. Dabei hat das Arrray die selbe Größe wie die Anzahl der Schritte die gemacht werden sollen plus einen, da dies der Ursprungspunkt (0,0) ist der immer gesetzt wird. Da wir nur ganzzahlige Werte übergeben, geben wir dem Array den Datentyp int.
In der for-Schleife, die so oft läuft wie wir der Methode Schritte übergeben, greifen wir im PositionsArray auf die Stelle step+1 zu (Array beginnt bei 0, an Stelle 0 liegt aber der Ursprung der nicht verändert wird) und speichern dort das Ergebnis der Addition des vorherigen und des aktuellen Schritts. Dies erreichen wir indem wir auf den vorherigen Schritt zugreifen der an der Stelle step im Array liegt, und auf den vorherigen Schritt den neuen Schritt addieren, den wir durch den Aufruf von singleStep erhalten.
Als Ergebnis der Methode erhalten wir ein Array mit step +1 einträgen, aus dem wir eine Grafik erstellen können um den Verlauf der Positionen aufzuzeigen. Dieses Array geben wir als Ergebnis der Methode in einem Tuple zusammen mit der Anzahl an Schritten die gemacht wurden zurück.
def drawWalk(tuple):
positionsArray = tuple[0]
fig = go.Figure(data=go.Scatter(
x=positionsArray[:, 0],
y=positionsArray[:, 1],
mode='markers',
name='Randomwalk in 2 Dimensions',
marker=dict(
color=numpy.arange(tuple[1]),
size=5,
colorscale='Greens',
showscale=True)
))
fig.show
Das von walkSteps erhaltene Tuple greifen wir in drawWalk wieder auf. Mit Hilfe von Plotly erstellen wir hier eine Grafik über die getätigten Schritte. Als Art des Plots wählen wir einen Scatterplot, dem wir die Werte aus dem Array zuweisen. Um dies so effizienz wie möglich zu gestalten bedienen wir uns einer Technik names Slicing hier mehr dazu. Wir weisen unserer Grafik einen Namen zu und legen ein Dictonary an um unsere Marker zu erstellen. Um die Punkte einzufärben verwenden wir das zweite Argument aus unserem Tuple, die Anzahl an gemachten Schritten.
def main():
drawWalk(walkSteps(1, 5000))
Zum ausführen des Programms schreiben wir uns eine main-Methode, in der wir unsere drawWalk-Methode aufrufen. In dieser rufen wir wiederum unsere walkSteps-Methode auf, der wir als Argumente für die Anzahl der Partikel 1 mitgeben und für die Anzahl der Schritte 50000.
import random as rd
import numpy
import plotly.graph_objects as go
maxStep = 5
numPart = 1
NORTH = 1; SOUTH = 2; WEST = 3; EAST = 4
def singleStep():
position = numpy.zeros(shape=(2,), dtype=int)
currentStep = rd.randint(1, 4 + 1)
position[1] += numpy.where(currentStep == NORTH, 1, 0)
position[1] -= numpy.where(currentStep == SOUTH, 1, 0)
position[0] += numpy.where(currentStep == EAST, 1, 0)
position[0] -= numpy.where(currentStep == WEST, 1, 0)
return position
def walkSteps(numPart=1, numstep=100):
positionsArray = numpy.zeros(shape=(numstep+1, 2), dtype=int)
for step in numpy.arange(numstep):
positionsArray[step+1, ] = positionsArray[step, ] + singleStep()
tuple = (positionsArray, numstep)
return tuple
def drawWalk(tuple):
positionsArray = tuple[0]
fig = go.Figure(data=go.Scatter(
x=positionsArray[:, 0],
y=positionsArray[:, 1],
mode='markers',
name='Randomwalk in 2 Dimensions',
marker=dict(
color=numpy.arange(tuple[1]),
size=5,
colorscale='Greens',
showscale=True)
))
fig.update_layout(
autosize=False,
height=800,
width=1310)
fig.show()
def main():
drawWalk(walkSteps(1, 5000))
if __name__ == "__main__":
main()
Fassen wir noch einmal zusammen: wir beginnen am Punkt (0,0) und machen einen Schritt in eine zufällige Richtung. Die Schritte haben alle immer die gleiche Wahrscheinlichkeit und unser Experiment funktioniert ohne Gedächtnis. Den neuen Schritt berechnen wir indem wir den erhaltenen zufälligen Schritt auf die vorherige Position addieren. Da wir keinen Seed verwenden erhalten wir immer wieder andere ZUfallszahlen. Dementsprechend verändert sich auch unser Bild mit jedem ausführen des Codes.
Zum 3 dimensionalen RandomWalk